#ifndef _RAY_OPENCL_OCLERROR_H_
#define _RAY_OPENCL_OCLERROR_H_

/*
* OpenCL error class to help decrypt error messages into a displayable string.
*
* Author: Radford Juang 
* Date:  4.27.2010
* Email: rayver /_at_/ hkn (dot) berkeley (dot) edu
*/

#include <string>
#include <exception>

#include <ray/opencl/OCLConstants.h>
#include <CL/cl.h>

#define ocl_check(code, msg)			{ int _errcode = code; if (_errcode != CL_SUCCESS) { throw OCLError(_errcode, msg); } }

#ifdef _DEBUG
	#define ocl_check_fast(code, msg)	ocl_check(code, msg)
#else
	#define ocl_check_fast(code, msg)	code
#endif

namespace ray { namespace opencl {

class OCLError  : std::exception
{
public:	
	cl_int			m_code;				//Error code value
	std::string		m_message;			//Message of the specific code
	std::string		m_notes;			//Additional notes about where error occurred

public:

	OCLError(cl_int errcode = CL_SUCCESS, const char *notes=NULL) {
		if (notes) m_notes = notes;

		m_code = errcode;
		m_message = parse(errcode);		
	}

    ~OCLError() throw() { }

	const cl_int err(void) const { return m_code; }

	const char *what() {
		return m_message.c_str();
	}

public:

	inline static const char *parse(int errcode) {
		const char *message="";

		#define DEF_MESSAGE(CODE, MSG)	case CODE: message = MSG; break		
		switch (errcode) {
			DEF_MESSAGE(CL_SUCCESS,							"SUCCESS");
			DEF_MESSAGE(CL_DEVICE_NOT_FOUND,				"DEVICE NOT FOUND");
			DEF_MESSAGE(CL_DEVICE_NOT_AVAILABLE,			"DEVICE NOT AVAILABLE");
			DEF_MESSAGE(CL_COMPILER_NOT_AVAILABLE,			"COMPILER NOT AVAILABLE");
			DEF_MESSAGE(CL_MEM_OBJECT_ALLOCATION_FAILURE,	"MEM OBJECT ALLOCATION FAILURE");
			DEF_MESSAGE(CL_OUT_OF_RESOURCES,				"OUT OF RESOURCES");
			DEF_MESSAGE(CL_OUT_OF_HOST_MEMORY,				"OUT OF HOST MEMORY");
			DEF_MESSAGE(CL_PROFILING_INFO_NOT_AVAILABLE,	"PROFILING INFO NOT AVAILABLE");
			DEF_MESSAGE(CL_MEM_COPY_OVERLAP,				"MEM COPY OVERLAP");
			DEF_MESSAGE(CL_IMAGE_FORMAT_MISMATCH,			"IMAGE FORMAT MISMATCH");
			DEF_MESSAGE(CL_IMAGE_FORMAT_NOT_SUPPORTED,		"IMAGE FORMAT NOT SUPPORTED");
			DEF_MESSAGE(CL_BUILD_PROGRAM_FAILURE,			"BUILD PROGRAM FAILURE");
			DEF_MESSAGE(CL_MAP_FAILURE,						"MAP FAILURE");
			DEF_MESSAGE(CL_INVALID_VALUE,					"INVALID VALUE");
			DEF_MESSAGE(CL_INVALID_DEVICE_TYPE,				"INVALID DEVICE TYPE");
			DEF_MESSAGE(CL_INVALID_PLATFORM,				"INVALID PLATFORM");
			DEF_MESSAGE(CL_INVALID_DEVICE,					"INVALID DEVICE");
			DEF_MESSAGE(CL_INVALID_CONTEXT,					"INVALID CONTEXT");
			DEF_MESSAGE(CL_INVALID_QUEUE_PROPERTIES,		"INVALID QUEUE PROPERTIES");
			DEF_MESSAGE(CL_INVALID_COMMAND_QUEUE,			"INVALID COMMAND QUEUE");
			DEF_MESSAGE(CL_INVALID_HOST_PTR,				"INVALID HOST PTR");
			DEF_MESSAGE(CL_INVALID_MEM_OBJECT,				"INVALID MEM OBJECT");
			DEF_MESSAGE(CL_INVALID_IMAGE_FORMAT_DESCRIPTOR,	"INVALID IMAGE FORMAT DESCRIPTOR");
			DEF_MESSAGE(CL_INVALID_IMAGE_SIZE,				"INVALID IMAGE SIZE");
			DEF_MESSAGE(CL_INVALID_SAMPLER,					"INVALID SAMPLER");
			DEF_MESSAGE(CL_INVALID_BINARY,					"INVALID BINARY");
			DEF_MESSAGE(CL_INVALID_BUILD_OPTIONS,			"INVALID BUILD OPTIONS");
			DEF_MESSAGE(CL_INVALID_PROGRAM,					"INVALID PROGRAM");
			DEF_MESSAGE(CL_INVALID_PROGRAM_EXECUTABLE,		"INVALID PROGRAM EXECUTABLE");
			DEF_MESSAGE(CL_INVALID_KERNEL_NAME,				"INVALID KERNEL NAME");
			DEF_MESSAGE(CL_INVALID_KERNEL_DEFINITION,		"INVALID KERNEL DEFINITION");
			DEF_MESSAGE(CL_INVALID_KERNEL,					"INVALID KERNEL");
			DEF_MESSAGE(CL_INVALID_ARG_INDEX,				"INVALID ARG INDEX");
			DEF_MESSAGE(CL_INVALID_ARG_VALUE,				"INVALID ARG VALUE");
			DEF_MESSAGE(CL_INVALID_ARG_SIZE,				"INVALID ARG SIZE");
			DEF_MESSAGE(CL_INVALID_KERNEL_ARGS,				"INVALID KERNEL ARGS");
			DEF_MESSAGE(CL_INVALID_WORK_DIMENSION,			"INVALID WORK DIMENSION");
			DEF_MESSAGE(CL_INVALID_WORK_GROUP_SIZE,			"INVALID WORK GROUP SIZE");
			DEF_MESSAGE(CL_INVALID_WORK_ITEM_SIZE,			"INVALID WORK ITEM SIZE");
			DEF_MESSAGE(CL_INVALID_GLOBAL_OFFSET,			"INVALID GLOBAL OFFSET");
			DEF_MESSAGE(CL_INVALID_EVENT_WAIT_LIST,			"INVALID EVENT WAIT LIST");
			DEF_MESSAGE(CL_INVALID_EVENT,					"INVALID EVENT");
			DEF_MESSAGE(CL_INVALID_OPERATION,				"INVALID OPERATION");
			DEF_MESSAGE(CL_INVALID_GL_OBJECT,				"INVALID GL OBJECT");
			DEF_MESSAGE(CL_INVALID_BUFFER_SIZE,				"INVALID BUFFER SIZE");
			DEF_MESSAGE(CL_INVALID_MIP_LEVEL,				"INVALID MIP LEVEL");
			DEF_MESSAGE(CL_INVALID_GLOBAL_WORK_SIZE,		"INVALID GLOBAL WORK SIZE");
			DEF_MESSAGE(ERR_COULD_NOT_OPEN_FILE,			"COULD NOT OPEN FILE");
			DEF_MESSAGE(ERR_FILE_NOT_FOUND,					"FILE NOT FOUND");
			default:	message = "UNKNOWN ERROR CODE";
		}
		return message;
		#undef DEF_MESSAGE
	}
};

}}


#endif
